
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        try {
            // 执行器，创建Promise对象时立即执行，
            // 执行器接受两个参数，执行成功时调用resolve，执行失败时调用reject
            executor(this.resolve, this.reject);
        } catch(e) {
            // 捕获执行器中发生的异常，执行失败调用 reject
            this.reject(e);
        }
    }

    // 一个 promise 有三种状态 pending, fulfilled, rejected 
    // 状态一旦确定就不能改变，且只能够由 pending 状态变成 rejected 或者 fulfilled 状态，
    // rejected 和 fulfilled 状态不能相互转换
    // promise初始状态为 pending
    status = PENDING;

    // 在executor函数中调用resolve时的参数值
    value = undefined;

    // 在executor函数中调用reject时的错误原因 或 executor执行异常时捕获的异常信息
    reason = undefined;

    // 成功回调队列
    successCallback = [];

    // 失败回调队列
    failedCallback = [];

    /**
     * 更改promise状态, 只能从PENDING -> FULFILLED
     * @param {*} value 成功回调函数的参数
     */
    resolve = (value) => {
        if (this.status !== PENDING) return;
        this.value = value;
        this.status = FULFILLED;

        // 从成功回调队列 执行 then方法传入 成功回调函数 
        while(this.successCallback.length) this.successCallback.shift()();
    }

    /**
     * 更改promise状态, 只能从PENDING -> REJECTED
     * @param {*} reason 失败回调函数的参数
     */
    reject = (reason) => {
        if (this.status !== PENDING) return;
        this.reason = reason;
        this.status = REJECTED;
        // 失败回调队列 执行 then方法传入 失败回调队列 
        while(this.failedCallback.length) this.failedCallback.shift()()
    }
    
    /**
     * 当前promise的执行结果,可以链式调用 需要返回一个新的Promise实例，
     * @param {Function} successCallback 成功回调函数 默认值是函数 可以把值传递下去
     * @param {Function} failedCallback 失败回调函数 默认值是函数 可以抛出异常原因
     * @returns {MyPromise}
     */
    then (successCallback = value => value, failedCallback = reason => {throw reason}){
        // 创建nextPromise
        let nextPromise = new MyPromise((resolve, reject) => {
            // 判断当前promise状态, 
            // FULFILLED 调用 成功回调函数
            // REJECTED 调用 失败回调函数
            // PENDING 等待中 需要将 successCallback 和 failedCallback 分别加入 成功回调队列 和 失败回调队列
            // successCallback 和 failedCallback 是异步任务，可以用setTimeout(fn, 0)模拟一个异步任务
            if (this.status === FULFILLED) {
                createAsyncTask(() => {
                    try {
                        // 获取successCallback()返回值
                        let x = successCallback(this.value);
                        // 判断返回值类型并 处理nextPromise 状态
                        resolvePromise(nextPromise, x, resolve, reject)
                    } catch(e) {
                        reject(e);
                    }
                })
            } else if (this.status === REJECTED) {
                createAsyncTask(() => {
                    try {
                        let x = failedCallback(this.reason);
                        resolvePromise(nextPromise, x, resolve, reject)
                    } catch(e) {
                        reject(e);
                    }
                })
            } else {
                this.successCallback.push(() => {
                    createAsyncTask(() => {
                        try {
                            let x = successCallback(this.value);
                            resolvePromise(nextPromise, x, resolve, reject)
                        } catch(e) {
                            reject(e);
                        }
                    })
                });
                this.failedCallback.push(() => {
                    createAsyncTask(() => {
                        try {
                            let x = failedCallback(this.reason);
                            resolvePromise(nextPromise, x, resolve, reject)
                        } catch(e) {
                            reject(e);
                        }
                    })
                });
            }
        });
        return nextPromise;
    }

    /**
     * 不论当前promise 对象的状态是 FULFILLED 和 REJECTED, callback总会执行
     * @param {*} callback 
     * @returns {MyPromise}
     */
    finally(callback) {
        // 用当前promise对象的then 方法取到回调返回值
        // 需要等待 callback() 执行完成，因此用 MyPromise.resolve处理一下
        return this.then((value) => {
            return MyPromise.resolve(callback()).then(() => value)
        }, (reason) => {
            return MyPromise.resolve(callback()).then(()=> {throw reason});
        })
    }

    /**
     * 捕获异常，执行失败回调
     * @param {*} failedCallback 
     * @returns {MyPromise}
     */
    catch(failedCallback) {
        return this.then(undefined, failedCallback)
    }

    /**
     * all 方法，顺序执行数组中的异步任务（普通对象或promise对象），得到一个有序的数组结果
     * @param {*} array 
     * @returns {MyPromise}
     */
    static all(array) {
        return new MyPromise((resolve, reject) => {
            let results = []; // 结果数组
            let index = 0; // 记录数组中的异步任务执行情况
            function addData(key, value) {
                results[key] = value;
                index++;
                // 判断异步任务执行完成没有，执行完成就调用resolve返回结果
                if (index === array.length) {
                    resolve(results)
                }
            }
            
            // 遍历数组
            for(let i  =0;i< array.length; i++) {
                let current = array[i];
                // 判断 current 是否是promise对象
                if (current instanceof MyPromise) {
                    // 当前promise对象的then 方法取到回调返回值 添加到结果中
                    current.then(value => addData(i, value), (reason) => reject(reason) )    
                } else {
                    // 普通对象直接添加到结果中
                    addData(i, array[i]);
                }
            }
        })
    }

    /**
     * 处理value，返回一个Promise对象 
     */
    static resolve(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value))
    }

    /**
     * 返回一个promise，一旦array的某个promise fulfilled 或 rejected, 返回的 promise就会fulfilled 或 rejected
     * @param {*} array 
     * @returns {MyPromise}
     */ 
    static race(array) {
        return new MyPromise((resolve, reject) => {
            for (let i = 0; i < array.length; i++) {
                 let current = array[i];
                 // 判断数组中的异步任务是否是promise对象
                 if (current instanceof MyPromise) {
                     // 调用current对象的then方法拿到状态，给返回的promise设置状态, 
                     current.then(resolve, reject);
                 } else {
                     // 普通对象直接resolve返回 退出循环
                     resolve(current);
                     break;
                 }
            }
        })
    }
}

function createAsyncTask(fn) {
    setTimeout(fn, 0)
}

function resolvePromise(nextPromise, x, resolve, reject) {
    // 禁止循环调用
    if (nextPromise === x) {
        return reject(new TypeError('Chaining cycle detected for MyPromise'))
    }
    if(x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x);
    }
}


module.exports = MyPromise;